#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "errormsg.h"
#include "absyn.h"
#include "types.h"
#include "table.h"
#include "symbol.h"
#include "env.h"
#include "irr/temp.h"
#include "irr/tree.h"
#include "irr/frame.h"
#include "irr/translate.h"

#define TRUE 1
#define FALSE 0

//int debugFlag = FALSE;
int debugFlag = TRUE;
int debugEnvFlag = FALSE;
//int debugEnvFlag = TRUE;
FILE* debugOut = NULL;

struct expty {Tr_exp exp; Ty_ty ty;};
struct expty expTy(Tr_exp exp, Ty_ty ty){
  struct expty e;
  e.exp = exp;
  e.ty = ty;
  return e;
}

struct expty SEM_transVar(S_table venv, S_table tenv, A_var v, Tr_level level);
struct expty SEM_transExp(S_table venv, S_table tenv, A_exp a, Tr_level level);
Tr_exp       SEM_transDec(S_table venv, S_table tenv, A_dec d, Tr_level level);
       Ty_ty SEM_transTy (              S_table tenv, A_ty a);



char* __get_Ty_ty_name_str(Ty_ty _ty){
  if(_ty == NULL){
    return "NULL";
  }
  else{
    switch(_ty->kind){
      case Ty_record:
        return "Ty_record";
      case Ty_nil:
        return "Ty_nil";
      case Ty_int:
        return "Ty_int";
      case Ty_string:
        return "Ty_string";
      case Ty_array:
        return "Ty_array";
      case Ty_name:
        return "Ty_name";
      case Ty_void:
        return "Ty_void";
      default:
        return "Unknown";
    }
  }
}
void __print_venv_KV(void *key, void *value){
  fprintf(debugOut, "\t");
  S_symbol _sym = key;
  E_enventry _ent = value;

  if(_sym == NULL){
    fprintf(debugOut, "symbol: NULL, ");
  }
  else{
    fprintf(debugOut, "symbol: %s, ", S_name(_sym));
  }

  if(_ent == NULL){
    fprintf(debugOut, "entry: NULL\n");
    return;
  }
  else{
    switch(_ent->kind){
        break;
      case E_varEntry:
        fprintf(debugOut, "entry: E_varEntry\n");
        break;
      case E_funEntry:
        fprintf(debugOut, "entry: E_funEntry\n");
        break;
      default:
        fprintf(debugOut, "entry: Ty_void\n");
        break;
    }
    return;
  }
}
void __print_tenv_KV(void *key, void *value){
  fprintf(debugOut, "\t");
  S_symbol _sym = key;
  Ty_ty _ty = value;

  if(_sym == NULL){
    fprintf(debugOut, "symbol: NULL, ");
  }
  else{
    fprintf(debugOut, "symbol: %s, ", S_name(_sym));
  }

  if(_ty == NULL){
    fprintf(debugOut, "type: NULL\n");
    return;
  }
  else{
    switch(_ty->kind){
        break;
      case Ty_record:
        fprintf(debugOut, "type: Ty_record\n");
        break;
      case Ty_nil:
        fprintf(debugOut, "type: Ty_nil\n");
        break;
      case Ty_int:
        fprintf(debugOut, "type: Ty_int\n");
        break;
      case Ty_string:
        fprintf(debugOut, "type: Ty_string\n");
        break;
      case Ty_array:
        fprintf(debugOut, "type: Ty_array\n");
        break;
      case Ty_name:
        fprintf(debugOut, "type: Ty_name\n");
        break;
      case Ty_void:
        fprintf(debugOut, "type: Ty_void\n");
        break;
      default:
        fprintf(debugOut, "type: Unknown\n");
        break;
    }
    return;
  }
}
void printDebugEnv(S_table venv, S_table tenv){
  if(debugOut == NULL){
    debugOut = stdout;
  }
  if(debugFlag){
    fprintf(debugOut,"=== print env ===\n");
    // void TAB_dump(TAB_table t, void (*show)(void *key, void *value));
    fprintf(debugOut,"tenv: \n");
    if(tenv) TAB_dump(tenv, __print_tenv_KV);
    fprintf(debugOut,"venv: \n");
    if(venv) TAB_dump(venv, __print_venv_KV);
    fprintf(debugOut,"=== ========= ===\n");
  }
}
void printDebugMsg(const char* msg, S_table venv, S_table tenv){
  if(debugOut == NULL){
    debugOut = stdout;
  }
  if(debugFlag){
    fprintf(debugOut, "Debug: %s\n", msg);
    if(debugEnvFlag) printDebugEnv(venv, tenv);
  }
}


Ty_tyList makeFormalTyList(S_table tenv, A_fieldList params);

Ty_tyList makeFormalTyList(S_table tenv, A_fieldList params){
  printDebugMsg("in makeFormalTyList", NULL, tenv);
  // HERE!!! makeFormalTyList shoud be written manually
  // HERE!!! makeFormalTyList 要检测dup和每个参数的类型是否在tenv里面
  A_fieldList params_backup = params;
  A_field cur_field = NULL;
  S_symbol nameList[127];
  S_symbol cur_name = NULL;
  S_symbol cur_type = NULL;
  int i = 0;
  int j = 0;
  Ty_ty ret = NULL;

  Ty_tyList result_dummy_head = checked_malloc(sizeof(*result_dummy_head));
  result_dummy_head->tail = NULL;
  Ty_tyList cur_pos_on_result = result_dummy_head;
  Ty_ty cur_ty_on_result = NULL;

  for(; params != NULL; params = params->tail){
    cur_field = params->head;
    cur_name = cur_field->name;
    cur_type = cur_field->typ;

    // check dup
    for(j = 0; j < i; j++){
      if(strcmp(S_name(nameList[i]), S_name(cur_name)) == 0){
        // dup found
        EM_error(cur_field->pos, "re-definition of parameter %s", S_name(cur_name));
      }
    }

    // check valid type
    ret = S_look(tenv, cur_type);
    if(ret == NULL){
      // type not found
      EM_error(cur_field->pos, "unknown type %s", S_name(cur_type));
    }

    // append to list
    cur_pos_on_result->tail = checked_malloc(sizeof(*cur_pos_on_result->tail));
    cur_pos_on_result = cur_pos_on_result->tail;
    cur_pos_on_result->tail = NULL;
    cur_pos_on_result->head = ret;

    nameList[i] = cur_name;
    i++;

  }

  return result_dummy_head->tail;
}

Ty_ty SEM_lookupType(S_symbol name, int pos, S_table tenv)
{
  printDebugMsg("in SEM_lookupType", NULL, NULL);
  Ty_ty type = S_look(tenv, name);
  
  if(type)
    type = Ty_Actual(type);
  else
    EM_error(pos, "undefined type '%s'", S_name(name));
  return type;
}

Ty_tyList SEM_getFormalTyList(A_fieldList params, int pos, S_table tenv)
{
  A_fieldList list=NULL;
  Ty_tyList tl = NULL, tlp = NULL;

  for(list = params; list; list = list->tail){
    A_field field = list->head;
    Ty_ty type = SEM_lookupType(field->typ, pos, tenv);
    if(!type)
      type = Ty_Int();
    if(tl){
      tlp = tlp->tail = Ty_TyList(type, NULL);
    }
    else
      tlp = tl = Ty_TyList(type, NULL);
  }
  return tl;
}

F_accessList SEM_getEscapeList(A_fieldList params)
{
  A_fieldList fl = params;
  F_accessList acsl = NULL, acslp = NULL;
  for(; fl; fl = fl->tail){
    F_access acs = InFrame(0); // MORE_DETAIL
    if(acsl)
      acslp = acslp->tail = F_AccessList(acs, NULL);
    else
      acslp = acsl = F_AccessList(acs, NULL);
  }
  return acsl;
}


struct expty SEM_transVar(S_table venv, S_table tenv, A_var v, Tr_level level){
  printDebugMsg("in SEM_transVar", venv, tenv);
  switch(v->kind){
    case A_simpleVar:{ 
      printDebugMsg("in case A_simpleVar", venv, tenv);
      E_enventry x = S_look(venv, v->u.simple);

      if(!x){
        EM_error(v->pos, "undefined var '%s'", S_name(v->u.simple));
        return expTy(Tr_Num(0), Ty_Int());
      }
      else if(x->kind != E_varEntry){
        EM_error(v->pos,
          "Expected '%s' to be a var, not a func",
          S_name(v->u.simple));
        return expTy(Tr_Num(0), Ty_Int());
      }

      return expTy(Tr_SimpleVar(x->u.var.access, level),
        Ty_Actual(x->u.var.ty));
    }
    case A_fieldVar:{
      printDebugMsg("in case A_fieldVar", venv, tenv);
      
      struct expty exp = SEM_transVar(venv, tenv, v->u.field.var, level);
      Ty_fieldList list = exp.ty->u.record;
      int i;

      if(exp.ty->kind != Ty_record){
        EM_error(v->pos, "Expected record type!");
        return expTy(Tr_Num(0), Ty_Int());
      }

      for(i = 0; list; list = list->tail, ++i){
        Ty_field field = list->head;
        if(field->name == v->u.field.sym){
          return expTy(Tr_FieldVar(exp.exp, i),
            Ty_Actual(field->ty));
        }
      }

      EM_error(v->pos, "there is no field named '%s'",
        S_name(v->u.field.sym));
      return expTy(Tr_Num(0), Ty_Int());
    }
    case A_subscriptVar:{
      printDebugMsg("in case A_subscriptVar", venv, tenv);
      
      A_var temp_v = v->u.subscript.var;
      A_exp temp_e = v->u.subscript.exp;

      struct expty temp_v_expty = SEM_transVar(venv, tenv, temp_v, level);
      Ty_ty type = temp_v_expty.ty;
      if(type->kind != Ty_array){
        EM_error(v->pos, "it is not an array type");
        return expTy(NULL, Ty_Int());
      }
      struct expty temp_e_expty = SEM_transExp(venv, tenv, temp_e, level);
      if(temp_e_expty.ty->kind != Ty_int)
        EM_error(v->pos, "Expected int type subscript!");
      return expTy(NULL, Ty_Actual(temp_v_expty.ty->u.array));
    }
  }
}


struct expty SEM_transIfExp(S_table venv, S_table tenv, struct expty cond, A_exp a, Tr_level level)
{
    EM_debug(1, "if_trans then");
    struct expty then = SEM_transExp(venv, tenv, a->u.iff.then, level);
    EM_debug(1, "if_trans if-then end");
    if(cond.ty->kind != Ty_int)
      EM_error(a->pos, "condition type must be int");
    if(a->u.iff.elsee){
      EM_debug(1, "if_trans else");
      struct expty elsee = SEM_transExp(venv, tenv, a->u.iff.elsee, level);
      if(!Ty_Match(then.ty, elsee.ty))
        EM_error(a->pos, "return Value of then&else must be of same type");
      return expTy(Tr_If(cond.exp, then.exp, elsee.exp), then.ty);
    }
    else if (then.ty->kind != Ty_void)
      EM_error(a->pos, "Format 'if then' should not return anything");
    return expTy(Tr_If(cond.exp, then.exp, NULL), Ty_Void());
}

struct expty SEM_transExp(S_table venv, S_table tenv, A_exp a, Tr_level level){
  printDebugMsg("in SEM_transExp", venv, tenv);
  if(!a){
    EM_debug(3, "Error: TransExp on NULL exp");
    struct expty exptyNull = expTy(NULL, Ty_Int());
    return exptyNull;
  }
  switch(a->kind){
    case A_opExp:{
      printDebugMsg("in case A_opExp", venv, tenv);
      A_oper oper = a->u.op.oper;
      struct expty left = SEM_transExp(venv, tenv, a->u.op.left, level);
      struct expty right = SEM_transExp(venv, tenv, a->u.op.right, level);

      switch(oper){
        case A_plusOp:
        case A_minusOp:
        case A_timesOp:
        case A_divideOp:{
          printDebugMsg("op: +-*/", venv, tenv);
          if(left.ty->kind != Ty_int)
            EM_error(a->u.op.left->pos, "integer required");
          if(right.ty->kind != Ty_int)
            EM_error(a->u.op.right->pos, "integer required");
          return expTy(
            Tr_Op(oper-A_plusOp+T_plus, left.exp, right.exp),
            Ty_Int());
        }


        case A_eqOp:
        case A_neqOp:{
          printDebugMsg("op: == !=", venv, tenv);
          Tr_exp res = NULL;
          if(!Ty_Match(left.ty, right.ty))
            EM_error(a->pos, "The type of two oprands not same");
          else if(left.ty->kind == Ty_string)
            res = Tr_StringRel(
              oper-A_eqOp+T_eq, left.exp, right.exp);
          else
            res = Tr_Rel(oper-A_eqOp+T_eq, left.exp, right.exp);
          return expTy(res, Ty_Int());
        }


        case A_ltOp:
        case A_leOp:
        case A_gtOp:
        case A_geOp:{
          printDebugMsg("op: < > <= >=", venv, tenv);
          Tr_exp res = NULL;
          if(!Ty_Match(left.ty, right.ty))
            EM_error(a->pos, "The type of two oprands not same");
          if(left.ty->kind != Ty_int && left.ty->kind != Ty_string)
            EM_error(a->pos, "type not of int or string cannot be compared");
          if(left.ty->kind == Ty_string)
            res = Tr_StringRel(
              oper-A_ltOp+T_ult, left.exp, right.exp);
          else
            res = Tr_Rel(oper-A_ltOp+T_ult, left.exp, right.exp);
          return expTy(res, left.ty);
        }

      }

/*      if(oper == A_plusOp){
        if(left.ty->kind != Ty_int)
          EM_error(a->u.op.left->pos, "integer required");
        if(right.ty->kind != Ty_int)
          EM_error(a->u.op.right->pos, "integer required");
        return expTy(tr_exp, Ty_Int());
      }
*/
      assert(0);
      return expTy(NULL, NULL);
    }
    case A_varExp:{
      printDebugMsg("in case A_varExp", venv, tenv);
      return SEM_transVar(venv, tenv, a->u.var, level);
    }
    case A_nilExp:{
      printDebugMsg("in case A_nilExp", venv, tenv);
      return expTy(Tr_Num(0), Ty_Nil());
    }
    case A_intExp:{
      printDebugMsg("in case A_intExp", venv, tenv);
      struct expty ret = expTy(Tr_Num(a->u.intt), Ty_Int());
      return ret;
    }
    case A_stringExp:{
      printDebugMsg("in case A_stringExp", venv, tenv);
      return expTy(Tr_String(a->u.stringg), Ty_String());
    }
    case A_callExp:{
      printDebugMsg("in case A_callExp", venv, tenv);

      char debugBuf[255] = { 0 };
      strcat(debugBuf, "\t\tCalling: ");
      strcat(debugBuf, S_name(a->u.call.func));
      printDebugMsg(debugBuf, NULL, NULL);

      S_symbol name = a->u.call.func;
      A_expList args = a->u.call.args;

      E_enventry ret = (E_enventry)S_look(venv, name);
      struct expty temp_expty;

      Ty_tyList tformals = ret->u.fun.formals;
      Tr_expList list=NULL, listp=NULL;

      if(ret == NULL){
        // name not found
        EM_error(a->pos, "undefined function %s", S_name(name));
        temp_expty = expTy(NULL, Ty_Int());
      }
      else if(ret->kind != E_funEntry){
        EM_error(a->pos, "undefined function %s", S_name(name));
        return expTy(NULL, Ty_Int());
      }
      else{
        int i;
        for(i = 1; args && tformals;
          tformals = tformals->tail, args = args->tail, i++){
          
          EM_debug(3, "call__loop_+");
          A_exp cur_exp = args->head;
          temp_expty = SEM_transExp(venv, tenv, cur_exp, level);
          if(!Ty_Match(tformals->head, temp_expty.ty))
            EM_error(a->pos,
                "passing argument %d of '%s' error: type error!",
                i, S_name(name));
          if(list){
            listp = listp->tail = Tr_ExpList(temp_expty.exp, NULL);
          }
          else
            listp = list = Tr_ExpList(temp_expty.exp, NULL);
          EM_debug(3, "call_loop_e");
        }
      }
      if(tformals)
        EM_error(a->pos, "expect more arguments!");
      if(args)
        EM_error(a->pos, "given two many arguments!");
      
      struct expty to_return = expTy(Tr_Call(level, // this need to be specified by var"ret", now it's uncorrect
                                             name,  // MORE_DETAIL
                                             list),
                                     Ty_Actual(ret->u.fun.result));
                        
      //printDebugMsg("=== going out case A_callExp ===", venv, tenv);
      
      return to_return;
    }
    case A_recordExp:{
      printDebugMsg("in case A_recordExp", venv, tenv);
      Ty_ty type = SEM_lookupType(a->u.record.typ, a->pos, tenv);
      Ty_fieldList rec_fields = type->u.record;
      A_efieldList rec_efields = a->u.record.fields;
      int size = 0;
      Tr_expList list = NULL, listp = NULL;

      if(!type)
        return expTy(NULL, Ty_Nil());
      if(type->kind != Ty_record)
        EM_error(a->pos, "'%s' is not of record type!",
          S_name(a->u.record.typ));


      for(; rec_fields && rec_fields->head && rec_efields->head;
        rec_fields = rec_fields->tail,
        rec_efields = rec_efields->tail, size++){
        EM_debug(2, "rec__loop_+");
        A_efield efield = rec_efields->head;
        struct expty exp = SEM_transExp(venv, tenv, efield->exp, level);
        EM_debug(2, "efiled trans over");
        if(!Ty_Match(rec_fields->head->ty, exp.ty))
          EM_error(a->pos, "field type wrong");
        EM_debug(2, "A_recordExp match end");
        if(list)
          listp = listp->tail = Tr_ExpList(exp.exp, NULL);
        else
          listp = list = Tr_ExpList(exp.exp, NULL);
        EM_debug(2, "rec__loop_e");
      }

      if(rec_fields != NULL && (rec_fields->head || rec_efields->head)){
        EM_error(a->pos, "field mismatch on number of items");      
      }
      EM_debug(1, "leave A_recordExp");
      return expTy(Tr_Record(list, size), type);
    }
    case A_seqExp:{
      printDebugMsg("in case A_seqExp", venv, tenv);
      
      A_expList l = a->u.seq;
      Tr_expList list = NULL, listp = list;

      /* luc's change in seqExp:
      EM_debug(1, "enter SEQ loop");
      for(; l; l = l->tail){
        EM_debug(2, "seq loop__+");
        struct expty cur_exp = SEM_transExp(venv, tenv, l->head, level);
        // TODO:: specify this label name
        EM_debug(3, "seq get label");
        cur_exp.exp->u.nx->u.LABEL = level->frame->name;
        if(list)
          listp = listp->tail = T_StmList(cur_exp.exp->u.nx, NULL);
        else
          listp = list = T_StmList(cur_exp.exp->u.nx, NULL);
        EM_debug(2, "seq loop_e");
        if(!l->tail)
          return expTy(Tr_Seq(list), cur_exp.ty);
      }
      return expTy(Tr_Num(0), Ty_Void());      
      */
      struct expty cur_exp = expTy(NULL, NULL);
      EM_debug(1, "enter SEQ loop");
      int times = 0;
      for(; l; l = l->tail){
        EM_debug(2, "seq loop__+");
        cur_exp = SEM_transExp(venv, tenv, l->head, level);
        printDebugMsg("=== HERE!!! ===", venv, tenv);
    
    // TODO:: specify this label name
        EM_debug(3, "seq get label");
        if(list)
          listp = (listp->tail = Tr_ExpList(cur_exp.exp, NULL));
        else
          listp = (list = Tr_ExpList(cur_exp.exp, NULL));
        EM_debug(2, "seq loop_e");
        //if(!l->tail)
          //return expTy(Tr_Seq(list), cur_exp.ty);
      }      
      
      printDebugMsg("=== going out case A_seqExp ===", venv, tenv);
      if (cur_exp.exp == NULL && cur_exp.ty == NULL){
        // have not go into the loop, return something empty
        printDebugMsg("=== returning something empry in A_seqExp ===", venv, tenv);
        return expTy(Tr_Num(0), Ty_Void()); 
      }
      else{
        return expTy(Tr_Seq(list), cur_exp.ty);        
      }
    }
    case A_assignExp:{
      printDebugMsg("in case A_assignExp", venv, tenv);

      A_var v = a->u.assign.var;
      A_exp e = a->u.assign.exp;
      struct expty left = SEM_transVar(venv, tenv, v, level);
      struct expty right = SEM_transExp(venv, tenv, e, level);

      if(!Ty_Match(left.ty, right.ty))
        EM_error(a->pos, "Type mismatch!");
/*
      if(left.ty->kind != right.ty->kind){
        // left and right not match
        EM_error(a->pos, "the left and right sides of assign expression do not match");
      }
*/
      if(a->u.assign.var->kind == A_simpleVar &&
        left.ty->kind == Ty_int){

        A_var av = a->u.assign.var;
        E_enventry entry = S_look(venv, av->u.simple);
        if(entry && entry->kind == E_varEntry
          && entry->u.var.ty);
      }

      if(left.exp == NULL) printDebugMsg("=== left ===", NULL, NULL); // @JinZiYang pay attention to this line, left.exp is NULL in running, please check your Tr_xxx functions
      //printDebugMsg("=== HERE ===", NULL, NULL);

      struct expty to_return = expTy(Tr_Assign(left.exp, right.exp), Ty_Void());

      printDebugMsg("=== end assignExp ===", NULL, NULL);

      return to_return;     

    }
    case A_ifExp:{
      printDebugMsg("in case A_ifExp", venv, tenv);

      struct expty cond, then, elsee;

      A_exp test = a->u.iff.test;
      if(test->kind == A_seqExp){
        EM_debug(2, "seq if");
        A_exp seqhead = test->u.seq->head;
        if(seqhead->kind == A_ifExp) {
          
          struct expty cond = SEM_transExp(venv, tenv, seqhead, level);
          return SEM_transIfExp(venv, tenv, cond, a, level);
        }
        goto InsideIfExp; // inside if exp
      }
      if (test->kind == A_ifExp){    
        EM_debug(2, "if , kind : %d", test->kind);
        EM_debug(1, "if_trans cond");
        struct expty cond = SEM_transExp(venv, tenv, test, level);
        return SEM_transIfExp(venv, tenv, cond, a, level);
      }
      else { // inside if exp
    InsideIfExp:
        EM_debug(2, "inside if exp");
        EM_debug(1, "if_trans cond");
        struct expty cond = SEM_transExp(venv, tenv, test, level);
        return SEM_transIfExp(venv, tenv, cond, a, level);
      }
      assert(0);
    }
    case A_whileExp:{
      // explanation :: only support one cond
      printDebugMsg("in case A_whileExp", venv, tenv);
      A_exp wtest = a->u.whilee.test;
      A_exp wbody = a->u.whilee.body;
      A_exp whead = wtest->u.seq->head;

      if(whead->kind == A_ifExp || whead->kind == A_opExp){ // two or more conds
        struct expty cond = SEM_transExp(venv, tenv, wtest, level);
        struct expty body = SEM_transExp(venv, tenv, wbody, level);

        EM_debug(1, "leave while exp");
        return expTy(Tr_While(cond.exp, body.exp), Ty_Void());
      }
      EM_debug(2, "while conds undefined!");
      assert(0);
    }
    case A_forExp:{
      printDebugMsg("in case A_forExp", venv, tenv);
      S_symbol avar_name = a->u.forr.var;
      A_exp alo = a->u.forr.lo;
      A_exp ahi = a->u.forr.hi;
      A_exp abody = a->u.forr.body;

      struct expty lo = SEM_transExp(venv, tenv, alo, level);
      struct expty hi = SEM_transExp(venv, tenv, ahi, level);
      struct expty body;
      Tr_access acs = Tr_allocLocal(level, a->u.forr.escape);

      if(lo.ty->kind != Ty_int)
        EM_error(a->pos, "lo exp should be int type");
      if(hi.ty->kind != Ty_int)
        EM_error(a->pos, "hi exp should be int type");
      S_beginScope(venv);
      S_enter(venv, a->u.forr.var, E_VarEntry(NULL, Ty_Int()));
      body = SEM_transExp(venv, tenv, abody, level);
      //
      S_endScope(venv);
      return expTy(Tr_For(acs, lo.exp, hi.exp, body.exp),
        Ty_Void());


/*      void* ret = S_look(venv, var_name);
      if(ret == NULL){
        // name not found
        EM_error(a->pos, "undefined variable %s", S_name(var_name));
        return expTy(tr_exp, Ty_Void());
      }

      SEM_transExp(venv, tenv, lo);
      SEM_transExp(venv, tenv, hi);
      SEM_transExp(venv, tenv, body);
      return expTy(tr_exp, Ty_Int());
*/
    }
    case A_breakExp:{
      printDebugMsg("in case A_breakExp", venv, tenv);
      // MORE_DETAIL: check if break inside while or for statement
      return expTy(NULL, Ty_Void());
    }
    case A_letExp:{
      printDebugMsg("in case A_letExp", venv, tenv);
      struct expty res;
      A_decList d;
      Tr_expList list = NULL;
      Tr_expList listp = NULL;

      S_beginScope(venv);
      S_beginScope(tenv);
      for(d = a->u.let.decs; d; d = d->tail){
        EM_debug(3, "let dec __loop_+");
        Tr_exp texp = SEM_transDec(venv, tenv, d->head, level);
        //if(!texp) /*if texp is NULL*/ printDebugMsg("=== texp is NULL!!! ===", venv, tenv);
        if(texp){      
          if(list){
            /* luc: changed
            listp = listp->tail = T_StmList(texp->u.nx, NULL);*/
            listp->tail = Tr_ExpList(texp, NULL);
            listp = listp->tail;
          }
          else{
            list = Tr_ExpList(texp, NULL);
            listp = list;
          }
        }
      }
      EM_debug(1, "leave let Loop");
      res = SEM_transExp(venv, tenv, a->u.let.body, level);
      //printDebugMsg("=-=-=-=-= HERE!!! =-=-=-=", NULL, NULL);
      
      // luc: bug is here!!!
      //if(res.exp) listp->tail = T_StmList(res.exp->u.nx, NULL);
      if(res.exp){
        if(!listp){
          /*if listp is NULL*/ 
          printDebugMsg("=== listp is NULL!!! ===", venv, tenv);
          list = Tr_ExpList(res.exp, NULL);
        } 
        else{
          listp->tail = Tr_ExpList(res.exp, NULL);
        }
      }
      
      
      res.exp = Tr_Seq(list);
      EM_debug(1, "leave env");
      S_endScope(tenv);
      S_endScope(venv);
      
      printDebugMsg("=== going out case A_letExp ===", venv, tenv);      
      
      return res;
    }
    case A_arrayExp:{
      printDebugMsg("in case A_arrayExp", venv, tenv);
      //printDebugEnv(venv, tenv);
      S_symbol name = a->u.array.typ;
      Ty_ty type = SEM_lookupType(name, a->pos, tenv);

      struct expty size = SEM_transExp(venv, tenv, a->u.array.size, level);
      struct expty init = SEM_transExp(venv, tenv, a->u.array.init, level);

      if(!type)
        return expTy(NULL, Ty_Int());
      if(type->kind != Ty_array)
        EM_error(a->pos,
          "'%s' is not an array type",
          S_name(a->u.array.typ));
      if(size.ty->kind != Ty_int)
        EM_error(a->pos, "array size not int");
      if(!Ty_Match(type->u.array, init.ty))
        EM_error(a->pos, "init array with wrong type"); // MORE_DETAIL
      return expTy(Tr_Array(size.exp, init.exp), type);
    }
  }

  assert(0);
}

Tr_exp SEM_transDec(S_table venv, S_table tenv, A_dec d, Tr_level level){
  printDebugMsg("in SEM_transDec", venv, tenv);
  switch(d->kind){
    case A_varDec:{ //uchk
      printDebugMsg("in case A_varDec", venv, tenv);

      struct expty init = SEM_transExp(venv, tenv, d->u.var.init, level);
      Ty_ty type = init.ty;
      Tr_access access = Tr_allocLocal(level, d->u.var.escape);

      if(d->u.var.typ){
        Ty_ty typechk = S_look(tenv, d->u.var.typ);
        if(typechk) typechk = Ty_Actual(typechk);
        if(!typechk) typechk = Ty_Int();
        if(!Ty_Match(typechk, type))
          EM_error(d->pos, "Init with incorrect type");
      }
      else if(type->kind == Ty_nil)
        EM_error(d->pos, "Error with taken specified record type");
      else if(type->kind == Ty_void)
        EM_error(d->pos, "Error: assign void value to a var");
      S_enter(venv, d->u.var.var, E_VarEntry(access, type));

      return Tr_Assign(Tr_SimpleVar(access, level), init.exp);
    }
    case A_functionDec:{
      printDebugMsg("in case A_functionDec", venv, tenv);
      
      A_fundecList fl = d->u.function;
      A_fundecList fundeclist = NULL;
      A_fundecList fundeclistp = NULL;
      struct expty res;

      EM_debug(2, "check re-def");

      // firstly, check if function re-defined
      for(fundeclist = fl; fundeclist && fundeclist->tail;
        fundeclist = fundeclist->tail){
        
        for(fundeclistp = fundeclist->tail; 
          fundeclistp; fundeclistp = fundeclistp->tail){
          
          A_fundec func = fundeclistp->head;
          if(fundeclist->head->name == func->name)
            EM_error(d->pos,
              "Function (%s) re-defined!",
              S_name(func->name));
        }
      }

      EM_debug(2, "enter Symbols");

      // enter into symbol table
      for(fundeclist = fl; fundeclist; fundeclist = fundeclist->tail){
        A_fundec func = fundeclist->head;
        Ty_tyList formals = SEM_getFormalTyList(func->params, d->pos, tenv);
        F_accessList escapes = SEM_getEscapeList(func->params);
        Ty_ty res;
        Temp_label label = Temp_newlabel();

        if(func->result){
          res = SEM_lookupType(func->result, d->pos, tenv);
          if(!res) res = Ty_Int();
        }
        else res = Ty_Void();
        S_enter(venv, func->name,
          E_FunEntry(Tr_newLevel(level, label, escapes),
            formals, res));
      }

      EM_debug(2, "check mutually recur");
      // check possible mutually defined functions
      for(fundeclist = fl; fundeclist; fundeclist = fundeclist->tail){
        EM_debug(3, "__loop_+");
        A_fundec func = fundeclist->head;
        E_enventry entry = S_look(venv, func->name);
        A_fieldList fl = func->params;
        Ty_tyList tyl = entry->u.fun.formals;
        Tr_accessList tacsl = Tr_formals(entry->u.fun.level);

        EM_debug(4, "begin scope");
        S_beginScope(venv);
        for(; fl && tyl && tacsl; fl = fl->tail,
                  tyl = tyl->tail,
                  tacsl = tacsl->tail){
          EM_debug(5, "enter__loop_+");
          S_enter(venv, fl->head->name,
            E_VarEntry(tacsl->head, tyl->head));
        }
        if(!fl) EM_debug(6, "no more fl");
        if(!tyl) EM_debug(6, "no more tyl");
        if(!tacsl) EM_debug(6, "no more tacsl");
        assert(!fl && !tyl);
        res = SEM_transExp(venv, tenv, func->body, entry->u.fun.level);
        if(!Ty_Match(res.ty, entry->u.fun.result)){
          EM_error(d->pos, "function return type wrong");
        }
        EM_debug(4, "end scope");
        S_endScope(venv);
        EM_debug(3, "__loop_e");
      }

      return NULL;
    }
    case A_typeDec:{
      printDebugMsg("in case A_typeDec", venv, tenv);

      A_nametyList nl = d->u.types;
      A_nametyList namelist = nl;
      A_nametyList namelistp = NULL;

      // check if re-defined
      for(; namelist && namelist->tail; namelist = namelist->tail){
        for(namelistp = namelist->tail; namelistp; namelistp = namelistp->tail){
          A_namety nt = namelistp->head;
          if(namelist->head->name == nt->name)
            EM_error(d->pos, "type '%s' re-defined!", S_name(nt->name));
        }
      }

      // firstly, just use the name, but leave type unchked
      for(namelist = nl; namelist; namelist = namelist->tail){
        A_namety nt = namelist->head;
        S_enter(tenv, nt->name, Ty_Name(nt->name, NULL));
      }

      // check the possible mutually defined type
      for(namelist = nl; namelist; namelist = namelist->tail){
        A_namety nt = namelist->head;
        Ty_ty type = S_look(tenv, nt->name);
        type->u.name.ty = SEM_transTy(tenv, nt->ty);
      }

      for(namelist = nl; namelist; namelist = namelist->tail){
        A_namety nt = namelist->head;
        Ty_ty type = S_look(tenv, nt->name);
        if(Ty_Actual(type) == type){
          EM_error(d->pos, "Error: Self recursive type '%s' !",
            S_name(nt->name));
        }
      }

      return NULL;
    }
  }
}

Ty_ty SEM_transTy(S_table tenv, A_ty a){
  printDebugMsg("in SEM_transTy", NULL, tenv);
  // HERE!!!
  // 分A_ty的类型来进行查找
  // 如果是record的话要看每个field的名字是否重复
  // 还有每个field的类型是否存在
  Ty_ty ty = NULL;
  char debugMsgBuf[511] = { 0 };

  switch(a->kind){
    case A_nameTy:{
      printDebugMsg("in case A_nameTy", NULL, tenv);
      S_symbol name = a->u.name;
      Ty_ty ret = S_look(tenv, name);
      if(ret == NULL){
        // not found
        ret = Ty_Int();
        EM_error(a->pos, "unknown type %s", S_name(name));
      }
      return ret;
    }
    case A_arrayTy:{ //uchk
      printDebugMsg("in case A_arrayTy", NULL, tenv);
      S_symbol name = a->u.array;
      Ty_ty ret = S_look(tenv, name);

      int smallDebug = FALSE;
      if(smallDebug){
        printDebugMsg("in case A_arrayTy", NULL, tenv);
        //printDebugEnv(NULL, tenv);

        strcat(debugMsgBuf, "S_symbol name: ");
        strcat(debugMsgBuf, S_name(name));

        strcat(debugMsgBuf, ", Ty_ty type: ");
        strcat(debugMsgBuf, __get_Ty_ty_name_str(ret));

        printDebugMsg(debugMsgBuf, NULL, tenv);
      }
      //if(ret == NULL || ret->kind != Ty_array){
      if(ret == NULL){
        // not found
        EM_error(a->pos, "unknown type %s", S_name(name));
      }
      return Ty_Array(ret);
    }
    case A_recordTy:{ //uchk
      printDebugMsg("in case A_recordTy", NULL, tenv);
      A_fieldList flist = a->u.record;
      Ty_fieldList list = NULL, listp = list;

      for(; flist; flist = flist->tail){
        A_field field = flist->head;
        Ty_ty type = S_look(tenv, field->typ);

        if(!type){
          EM_error(a->pos, "undefined type '%s'", S_name(field->typ));
          type = Ty_Int();
        }
        if(list)
          listp = listp->tail = Ty_FieldList(Ty_Field(field->name, type), NULL);
        else
          listp = list = Ty_FieldList(Ty_Field(field->name, type), NULL);
      }
      return Ty_Record(list);
    }
  }

  return ty;
}



// top proc
void SEM_transProg(A_exp exp){
  printDebugMsg("in SEM_transProg", NULL, NULL);
  S_table venv = E_base_venv();
  S_table tenv = E_base_tenv();

  struct expty res;

  res = SEM_transExp(venv, tenv, exp, Tr_outermost());
  
  printDebugMsg("=== starts Tr_printExp ===", venv, tenv);        

  //SEM_transExp(venv, tenv, exp);
  Tr_printExp(res.exp);

  printDebugMsg("=== going out SEM_transProg ===", venv, tenv);        
}

